# 导入库

# 导入sklearn的数据集
from sklearn.datasets import load_iris
# 切分数据集和训练集
from sklearn.model_selection import train_test_split
# 计算分类预测准确率
from sklearn.metrics import accuracy_score

iris = load_iris()

x = iris.data
y = iris.target.reshape(-1, 1)

# 换分训练集和测试集
x_train, x_test, y_train, y_test = train_test_split(
    x, y, test_size=0.3,random_state=35, stratify = y)


import numpy as np

# 距离函数定义
# 曼哈顿距离
def l1_distance(a, b):
    return np.sum(np.abs(a - b), axis=1)


def l2_distance(a, b):
    return np.sqrt(np.sum((a - b) ** 2, axis=1))


# 分类器实现
class kNN(object):
    # 定义初始化方法,初始化kNN需要的超参数
    def __init__(self, n_neighbors=1, dist_func=l1_distance):
        self.n_neighbors = n_neighbors
        self.dist_func = dist_func

    # 训练模型方法
    def fit(self, x, y):
        # 将x，y传进来即可
        self.x_train = x
        self.y_train = y

    # 模型预测方法
    def predict(self, x):
        # 初始化预测分类数组
        y_pred = np.zeros((x.shape[0], 1), dtype=self.y_train.dtype)

        # 遍历输入的x数据点
        for i, x_test in enumerate(x):
            # x_test跟所有的训练数据计算距离
            distances = self.dist_func(self.x_train, x_test)

            # 得到的距离按照由近到远排序
            nn_index = np.argsort(distances)

            # 选取最近的k个点，保存其类别
            nn_y = self.y_train[nn_index[:self.n_neighbors]].ravel()

            # 统计类别中频率最高的那个，赋给y_pred[i]
            y_pred[i] = np.argmax(np.bincount(nn_y))
        return y_pred



# 定义一个 knn
def checkAccuracy(n_neighbors=3):
    knn = kNN(n_neighbors=n_neighbors)

    # 训练模型
    knn.fit(x_train, y_train)

    # 传入测试数据，做预测
    y_pred = knn.predict(x_test)

    # 求准确率
    accuracy = accuracy_score(y_test, y_pred)
    print('n_neighbors: %d  预测准确率: %.2f ' %  (n_neighbors, accuracy) )

    #
    return accuracy

xAxis = []
yAxis = []
for i in range(1, 11):
    acc = checkAccuracy(n_neighbors=i)
    xAxis.append(i)
    yAxis.append(acc)

from matplotlib import pyplot as plt

plt.plot(xAxis, yAxis)

# for i in range(len(xAxis)):
#     plt.text(xAxis[i], yAxis[i], f'({xAxis[i]}, {yAxis[i]})', color='blue', fontsize=10)

plt.show()